package com.runa.monitor.platinterface.backwork.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.runa.monitor.comm.dto.Meter;
import com.runa.monitor.comm.dto.Node;
import com.runa.monitor.comm.dto.TempPanel;
import com.runa.monitor.comm.dto.Valve;
import com.runa.monitor.comm.dto.newComm.*;
import com.runa.monitor.comm.entity.CommMeter;
import com.runa.monitor.comm.entity.CommPanel;
import com.runa.monitor.comm.entity.CommValve;
import com.runa.monitor.platinterface.backwork.function.AbstractTaskFunction;
import com.runa.persistence.util.SpringContextUtil;
import com.runa.monitor.platinterface.mqtt.MqttUtil;
import com.runa.monitor.platinterface.netservice.connect.commstatus.CommStatus;
import com.runa.monitor.platinterface.platService.RepositoryService;
import com.runa.persistence.StringUtil;
import com.runa.rccprotocol.dto.AffirmDeny;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collections;
import java.util.List;

@Slf4j
public class SubEquReadTaskFunction extends AbstractTaskFunction<List<Node>, Object, Void> {

    private final String task;
    private final String netEquId;
    private final String holdInfo;
    private final int priority;
    private List<Node> nodes;
    private final RepositoryService repositoryService;

    public SubEquReadTaskFunction(String task, String netEquId, String holdInfo, int priority) {
        this.task = task;
        this.netEquId = netEquId;
        this.holdInfo = holdInfo;
        this.priority = priority;
        this.repositoryService = SpringContextUtil.getBean(RepositoryService.class);
    }

    @Override
    public void setStatus(List<Node> nodes) {
        this.nodes = nodes;
    }

    @Override
    public void doApply(CommStatus status, Object data) {
        log.info("SubEquReadTaskFunction {} {} {} {}", task, netEquId, status, JSONObject.toJSONString(data));
        if (CommStatus.OK.equals(status)) {
            if (data instanceof List) {
                if (((List) data).isEmpty()) {
                    unreadBack();
                } else {
                    List<CurrentData> currentDataList = (List<CurrentData>) data;
                    for (CurrentData currentData : currentDataList) {
                        try {
                            if (currentData instanceof HotMeterVO) {
                                meterDataBack((HotMeterVO) currentData);
                            } else if (currentData instanceof ValveVO) {
                                valveDataBack((ValveVO) currentData);
                            } else if (currentData instanceof PanelVO) {
                                panelDataBack(null, (PanelVO) currentData);
                            }
                        } catch (Exception e) {
                            log.error("SubEquReadTaskFunction DoApply Error: ", e);
                        }
                    }
                }
            } else if (data instanceof AffirmDeny) {
                anomalyBack((AffirmDeny) data);
            }
        } else if (CommStatus.OffLine.equals(status)) {
            if (data instanceof List) {
                List<Node> nodeList = (List<Node>) data;
                offlineReadBack(nodeList);
            }
        } else if (CommStatus.TimeOut.equals(status)) {
            timeoutBack();
        }
    }

    private void meterDataBack(HotMeterVO hotMeterVO) {
        AsynResult asynResult = AsynResult.success();
        asynResult.setTask(task);
        asynResult.setHoldInfo(holdInfo);
        if (hotMeterVO.getSuccess()) {
            if (hotMeterVO.getFlow() != null) {
                hotMeterVO.setFlow(BigDecimal.valueOf(hotMeterVO.getFlow())
                        .setScale(2, RoundingMode.HALF_DOWN).floatValue());
            }
            asynResult.setContent(hotMeterVO);
        } else {
            asynResult.unreadSetting();
        }
        CommMeter commMeter = null;
        if (StringUtil.isNotBlank(hotMeterVO.getMeterSerial())) {
            commMeter = repositoryService.getCommMeterByNodeSerialForNetEqu(hotMeterVO.getMeterSerial(), netEquId);
        } else if (hotMeterVO.getIndexNo() != null) {
            commMeter = repositoryService.getCommMeterByNodeIndexForNetEqu(hotMeterVO.getIndexNo(), netEquId);
        }
        if (commMeter != null) {
            asynResult.setIdentity(commMeter.getId());
        } else {
            asynResult.noSuchFileSetting();
        }
        mqttSend(HotMeterVO.TOPIC, asynResult);
    }

    private void valveDataBack(ValveVO valveVO) {
        AsynResult asynResult = AsynResult.success();
        asynResult.setTask(task);
        asynResult.setHoldInfo(holdInfo);
        if (valveVO.getSuccess()) {
            asynResult.setContent(valveVO);
        } else {
            asynResult.unreadSetting();
        }
        CommValve commValve;
        if (StringUtil.isNotBlank(valveVO.getValveSerial())) {
            commValve = repositoryService.getCommValveByNodeSerialForNetEqu(valveVO.getValveSerial(), netEquId);
        } else if (valveVO.getIndex() != null) {
            commValve = repositoryService.getCommValveByNodeIndexForNetEqu(valveVO.getIndex(), netEquId);
        } else {
            commValve = null;
        }
        if (commValve != null) {
            asynResult.setIdentity(commValve.getId());
            if (valveVO.getPanelVOList() != null) {
                valveVO.getPanelVOList().forEach(panel -> panelDataBack(commValve.getSerial(), panel));
                valveVO.setPanelVOList(Collections.emptyList());
            }
        } else {
            asynResult.noSuchFileSetting();
        }
        mqttSend(ValveVO.TOPIC, asynResult);
    }

    private void panelDataBack(String valveSerial, PanelVO panelVO) {
        AsynResult asynResult = AsynResult.success();
        asynResult.setTask(task);
        asynResult.setHoldInfo(holdInfo);
        if (panelVO.getSuccess()) {
            asynResult.setContent(Collections.singletonList(panelVO));
        } else {
            asynResult.unreadSetting();
        }
        CommPanel commPanel = null;
        if (StringUtil.isNotBlank(panelVO.getSerial())) {
            commPanel = repositoryService.getCommPanelByNodeSerialForNetEqu(panelVO.getSerial(), netEquId);
        } else if (StringUtil.isNotBlank(valveSerial)) {
            commPanel = repositoryService.getCommPanelByNodeSerialForNetEqu(valveSerial, netEquId);
        } else if (panelVO.getValveIndex() != null) {
            commPanel = repositoryService.getCommPanelByNodeIndexForNetEqu(panelVO.getValveIndex(), netEquId);
        }
        if (commPanel != null) {
            asynResult.setIdentity(commPanel.getId());
        } else {
            asynResult.noSuchFileSetting();
        }
        mqttSend(PanelVO.TOPIC, asynResult);
    }

    private void anomalyBack(AffirmDeny affirmDeny) {
        if (!affirmDeny.getData() && nodes != null) {
            AsynResult asynResult = new AsynResult();
            for (Node node : nodes) {
                asynResult.setTask(task);
                asynResult.setHoldInfo(holdInfo);
                asynResult.setIdentity(node.getId());
                asynResult.denySetting();
                mqttSend(getTopic(node), asynResult);
            }
        }
    }

    private void offlineReadBack(List<Node> nodeList) {
        AsynResult asynResult = new AsynResult();
        for (Node node : nodeList) {
            asynResult.setTask(task);
            asynResult.setHoldInfo(holdInfo);
            asynResult.setIdentity(node.getId());
            asynResult.offlineSetting();
            mqttSend(getTopic(node), asynResult);
        }
    }

    private void timeoutBack() {
        AsynResult asynResult = new AsynResult();
        for (Node node : nodes) {
            asynResult.setTask(task);
            asynResult.setHoldInfo(holdInfo);
            asynResult.setIdentity(node.getId());
            asynResult.timeoutSetting();
            mqttSend(getTopic(node), asynResult);
        }
    }

    private void unreadBack() {
        AsynResult asynResult = new AsynResult();
        for (Node node : nodes) {
            asynResult.setTask(task);
            asynResult.setHoldInfo(holdInfo);
            asynResult.setIdentity(node.getId());
            asynResult.unreadSetting();
            mqttSend(getTopic(node), asynResult);
        }
    }

    private void mqttSend(String topic, AsynResult asynResult) {
        log.debug("SubEquReadTaskFunction.mqttSend: {} {}", topic, JSON.toJSONString(asynResult));
        MqttUtil.send(topic, asynResult);
    }

    private String getTopic(Node node) {
        String topic = null;
        if (node instanceof Meter) {
            topic = HotMeterVO.TOPIC;
        }
        if (node instanceof Valve) {
            topic = ValveVO.TOPIC;
        }
        if (node instanceof TempPanel) {
            topic = PanelVO.TOPIC;
        }
        return topic;
    }

    @Override
    public Void waitResult() {
        return null;
    }

    @Override
    public String getSerial() {
        return null;
    }

    @Override
    public int getPriority() {
        return priority;
    }

    @Override
    public String getTask() {
        return task;
    }
}
